/*
 * FindBugs - Find Bugs in Java programs
 * Copyright (C) 2003-2008 University of Maryland
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.gui2;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.event.TreeModelEvent;
import javax.swing.tree.TreePath;

import edu.umd.cs.findbugs.I18N;
import edu.umd.cs.findbugs.filter.Filter;
import edu.umd.cs.findbugs.filter.Matcher;
import edu.umd.cs.findbugs.gui2.BugTreeModel.TreeModification;
import edu.umd.cs.findbugs.gui2.IApplicationState.UIFactory;
import edu.umd.cs.findbugs.gui2.sort.SortableBugTree;

/**
 * @author graham
 */
public class BugTreePopupProvider {

	private JPopupMenu bugPopupMenu;
	private JPopupMenu branchPopupMenu;
	private final IApplicationState appState;
	private SortableBugTree bugTree;
	private final UIFactory uiFactory;
	private final MainFrame mainFrame;
	
	public static BugTreePopupProvider popupProviderPrototype(IApplicationState appState, UIFactory uiFactory, MainFrame mainFrame) {
		return new BugTreePopupProvider(appState, uiFactory, mainFrame);
	}
	
    public BugTreePopupProvider(IApplicationState appState, UIFactory uiFactory, MainFrame mainFrame) {
    	this.appState = appState;
		this.uiFactory = uiFactory;
		this.mainFrame = mainFrame;
		
		bugPopupMenu = createBugPopupMenu();
		branchPopupMenu = createBranchPopUpMenu();
    }
    
    public void setBugTree(SortableBugTree bugTree) {
    	this.bugTree = bugTree;
    }
    
    /**
	 * Creates popup menu for bugs on tree.
	 * @return
	 */
	private JPopupMenu createBugPopupMenu() {
		JPopupMenu popupMenu = new JPopupMenu();
		popupMenu.add(makeFilterBugMenu());
		popupMenu.add(makeChangeDesignationMenu());
		return popupMenu;
	}

	private JMenuItem makeFilterBugMenu() {
		JMenuItem filterMenuItem = MainFrame.newJMenuItem("menu.filterBugsLikeThis", "Filter bugs like this");

		filterMenuItem.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent evt){
				appState.saveComments();
				new NewFilterFromBug(appState.currentSelectedBugLeaf().getBug(), uiFactory);

				appState.setProjectChanged(true);
				bugTree.setSelectionRow(0); //Selects the top of the Jtree so the CommentsArea syncs up.
			}
		});
		return filterMenuItem;
	}
	
	/**
	 * Creates the branch pop up menu that ask if the user wants 
	 * to hide all the bugs in that branch.
	 * @return
	 */
	private JPopupMenu createBranchPopUpMenu(){
		JPopupMenu popupMenu = new JPopupMenu();
		popupMenu.add(makeFilterBranchMenu());
		popupMenu.add(makeChangeDesignationMenu());
		return popupMenu;
	}

	private JMenuItem makeFilterBranchMenu() {
		JMenuItem filterMenuItem = MainFrame.newJMenuItem("menu.filterTheseBugs", "Filter these bugs");

		filterMenuItem.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent evt)
			{
				//TODO This code does a smarter version of filtering that is only possible for branches, and does so correctly
				//However, it is still somewhat of a hack, because if we ever add more tree listeners than simply the bugtreemodel,
				//They will not be called by this code.  Using FilterActivity to notify all listeners will however destroy any
				//benefit of using the smarter deletion method.
				
				try {
					appState.saveComments();
					int startCount;
					TreePath path = bugTree.getSelectionPath();
					TreePath deletePath = path;
					startCount = ((BugAspects) (path.getLastPathComponent())).getCount();
					int count = ((BugAspects) (path.getParentPath().getLastPathComponent())).getCount();
				while(count==startCount)
				{
					deletePath=deletePath.getParentPath();
					if (deletePath.getParentPath()==null)//We are at the top of the tree, don't let this be removed, rebuild tree from root.
					{
						Matcher m = appState.currentSelectedBugAspects().getMatcher();
						Filter suppressionFilter = appState.getProject().getSuppressionFilter();
						suppressionFilter.addChild(m);
						uiFactory.preferencesFrame().updateFilterPanel();
						uiFactory.filterActivity().notifyListeners(FilterListener.Action.FILTERING, null);
						return;
					}
					count=((BugAspects)(deletePath.getParentPath().getLastPathComponent())).getCount();
				} 
/*				 
				deletePath should now be a path to the highest 
				ancestor branch with the same number of elements
				as the branch to be deleted
				in other words, the branch that we actually have 
				to remove in order to correctly remove the selected branch.
*/
				BugTreeModel model = bugTree.bugTreeModel();
				TreeModelEvent event=new TreeModelEvent(this,deletePath.getParentPath(),
					new int[]{model.getIndexOfChild(deletePath.getParentPath().getLastPathComponent(),deletePath.getLastPathComponent())},
					new Object[]{deletePath.getLastPathComponent()});
				Matcher m = appState.currentSelectedBugAspects().getMatcher();
				Filter suppressionFilter = appState.getProject().getSuppressionFilter();
				suppressionFilter.addChild(m);
				uiFactory.preferencesFrame().updateFilterPanel();
				model.sendEvent(event, TreeModification.REMOVE);
//				FilterActivity.notifyListeners(FilterListener.Action.FILTERING, null);
				
				appState.setProjectChanged(true);
				
				bugTree.setSelectionRow(0);//Selects the top of the Jtree so the CommentsArea syncs up.
				} catch (RuntimeException e) {
					uiFactory.showMessageDialog("Unable to create filter: " + e.getMessage());
				}
			}
		});
		return filterMenuItem;
	}

	private JMenu makeChangeDesignationMenu() {
		JMenu changeDesignationMenu = MainFrame.newJMenu("menu.changeDesignation", "Change bug designation");

		int i = 0;
		int keyEvents [] = {KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_3, KeyEvent.VK_4, KeyEvent.VK_5, KeyEvent.VK_6, KeyEvent.VK_7, KeyEvent.VK_8, KeyEvent.VK_9};
		for(String key :  I18N.instance().getUserDesignationKeys(true)) {
			String name = I18N.instance().getUserDesignation(key);
			mainFrame.addDesignationItem(changeDesignationMenu, name, keyEvents[i++]);
		}
		return changeDesignationMenu;
	}

	
    public void showBranchPopupMenu(Component parent, int x, int y) {
	    branchPopupMenu.show(parent, x, y);
    }

    public void showBugPopupMenu(Component parent, int x, int y) {
	    bugPopupMenu.show(parent, x, y);
    }

    public void setPopupMenuFonts(float size) {
    	bugPopupMenu.setFont(bugPopupMenu.getFont().deriveFont(size));
		uiFactory.setFontSizeHelper(size, bugPopupMenu.getComponents());

		branchPopupMenu.setFont(branchPopupMenu.getFont().deriveFont(size));
		uiFactory.setFontSizeHelper(size, branchPopupMenu.getComponents());
    }
    
    public BugTreePopupProvider cloneForTree(SortableBugTree bugTree) {
    	BugTreePopupProvider popupProvider = new BugTreePopupProvider(appState, uiFactory, mainFrame);
    	popupProvider.setBugTree(bugTree);
		return popupProvider;
    }

}
